#include <stdio.h>
#include <tremo_cm4.h>
#include "tremo_regs.h"
#include "tremo_pwr.h"
#include "tremo_lcd.h"
#include "tremo_rcc.h"
#include "tremo_delay.h"
#include "tremo_gpio.h"
#include "tremo_uart.h"
#include "tremo_lptimer.h"
#include "main.h"

typedef enum wake_source_e {
    WS_LPTIM_ARR,
    WS_LPTIM_UNDEF
} wake_soruce_t;

static uint32_t gv_wake_source;

void lptimer_encoder()
{
    lptimer_init_t lptimer_init_config = {0};

    gpio_init(PULSE_IA_GPIO, PULSE_IA_PIN, PULSE_INPUT_MODE);
    gpio_init(PULSE_IB_GPIO, PULSE_IB_PIN, PULSE_INPUT_MODE);
    gpio_set_iomux(PULSE_IA_GPIO, PULSE_IA_PIN, PULSE_IA_AFUN);
    gpio_set_iomux(PULSE_IB_GPIO, PULSE_IB_PIN, PULSE_IB_AFUN);

    lptimer_deinit(PULSE_COUNT_LPTIMER_SEL);
    PULSE_COUNT_LPTIMER_CLK_SET_FUNC(PULSE_COUNT_LPTIMER_CLK_SOURCE);
    rcc_enable_peripheral_clk(PULSE_COUNT_LPTIMER_CLK, true);
    lptimer_cmd(PULSE_COUNT_LPTIMER_SEL, DISABLE);

    lptimer_init_config.count_by_external  = false;
    lptimer_init_config.prescaler          = PULSE_COUNT_LPTIMER_PRESCALER;
    lptimer_init_config.autoreload_preload = false;
    lptimer_init_config.wavpol_inverted    = false;
    lptimer_init(PULSE_COUNT_LPTIMER_SEL, &lptimer_init_config);

    lptimer_config_trigger_filter(PULSE_COUNT_LPTIMER_SEL, LPTIMER_TRGFLT_4);
    lptimer_config_encoder(PULSE_COUNT_LPTIMER_SEL, ENABLE);
    lptimer_config_clock_polarity(PULSE_COUNT_LPTIMER_SEL, PULSE_COUNT_LPTIMER_CKPOL);
    lptimer_config_interrupt(PULSE_COUNT_LPTIMER_SEL, LPTIMER_IT_ARRM, ENABLE);
    lptimer_config_wakeup(PULSE_COUNT_LPTIMER_SEL, LPTIMER_CFGR_ARRM_WKUP, ENABLE);
    lptimer_config_wakeup(PULSE_COUNT_LPTIMER_SEL, LPTIMER_CFGR_UP_WKUP, ENABLE);
    lptimer_cmd(PULSE_COUNT_LPTIMER_SEL, ENABLE);
    lptimer_config_count_mode(PULSE_COUNT_LPTIMER_SEL, LPTIMER_MODE_CNTSTRT, ENABLE);
    lptimer_set_arr_register(PULSE_COUNT_LPTIMER_SEL, PULSE_COUNT_LPTIMER_ARR_VAL);

    NVIC_EnableIRQ(PULSE_COUNT_LPTIMER_IRQ);
    NVIC_SetPriority(PULSE_COUNT_LPTIMER_IRQ, PULSE_COUNT_LPTIMER_IRQ_PRIORITY);

    printf("ISR: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->ISR);
    printf("ICR: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->ICR);
    printf("IER: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->IER);
    printf("CFGR:%8lx\r\n", PULSE_COUNT_LPTIMER_SEL->CFGR);
    printf("CR:  %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->CR);
    printf("CMP: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->CMP);
    printf("ARR: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->ARR);
    printf("CNT: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->CNT);
    printf("CSR: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->CSR);
    printf("SR1: %8lx\r\n", PULSE_COUNT_LPTIMER_SEL->SR1);
}

void uart_log_init(void)
{
    /* must reset uart after wake up form stop3 */
    uart_deinit(LOG_UART_SEL);
    rcc_enable_peripheral_clk(LOG_UART_CLK, true);

    /* uart used gpio */
    gpio_set_iomux(LOG_UART_TX_GPIO, LOG_UART_TX_PIN, LOG_UART_TX_AFUN);
    gpio_set_iomux(LOG_UART_RX_GPIO, LOG_UART_RX_PIN, LOG_UART_RX_AFUN);

    /* uart config struct init */
    uart_config_t uart_config = {0};
    uart_config_init(&uart_config);

    uart_config.baudrate = LOG_UART_BAUDRATE;
    uart_init(LOG_UART_SEL, &uart_config);
    uart_cmd(LOG_UART_SEL, ENABLE);
}

int main(void)
{
    /* enable the low external 32k osc */
    rcc_enable_oscillator(RCC_OSC_XO32K, true);

    rcc_enable_peripheral_clk(RCC_PERIPHERAL_AFEC, true);
    rcc_enable_peripheral_clk(RCC_PERIPHERAL_PWR, true);
    rcc_enable_peripheral_clk(RCC_PERIPHERAL_GPIOA, true);
    rcc_enable_peripheral_clk(RCC_PERIPHERAL_GPIOB, true);
    rcc_enable_peripheral_clk(RCC_PERIPHERAL_GPIOC, true);
    rcc_enable_peripheral_clk(RCC_PERIPHERAL_GPIOD, true);

    uart_log_init();
    printf("started\n");
    lptimer_encoder();
    delay_ms(1000);

    /* Infinite loop */
    while (1) {
        /* disable systick irq */
        suspend_tick();
        /* clear possible pending systick irq mask */
        SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
        /* enter sleep */
        pwr_deepsleep_wfi(PWR_LP_MODE_STOP3);

        /* resume systick irq */
        resume_tick();
        /* re-init log uart */
        uart_log_init();

        if (gv_wake_source == WS_LPTIM_ARR) {
            printf("LPTIM ARR\n");
        } else if (gv_wake_source == WS_LPTIM_UNDEF) {
            printf("LPTIM UNDEF\n");
            while (1);
        } else {
            while (1);
        }
    }

}

/**
  * @brief  This function handles PULSE_COUNT_LPTIMER_SEL Handler.
  * @param  None
  * @retval None
  */
void PULSE_COUNT_LPTIMER_IRQHandler(void)
{
    if (lptimer_get_interrupt_status(PULSE_COUNT_LPTIMER_SEL, LPTIMER_IT_ARRM)) {
        lptimer_clear_interrupt(PULSE_COUNT_LPTIMER_SEL, LPTIMER_IT_ARRM);
        gv_wake_source = WS_LPTIM_ARR;
    } else {
        lptimer_clear_interrupt(PULSE_COUNT_LPTIMER_SEL, LPTIMER_IT_ARRM);
        gv_wake_source = WS_LPTIM_UNDEF;
    }
}

#ifdef USE_FULL_ASSERT
void assert_failed(void* file, uint32_t line)
{
    (void)file;
    (void)line;

    while (1) { }
}
#endif
